package tech.xs.auth.sync.mq;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import tech.xs.auth.domain.entity.AuthPathPermission;
import tech.xs.auth.domain.entity.AuthPathRole;
import tech.xs.auth.domain.entity.AuthPathWhite;
import tech.xs.auth.service.AuthPathPermissionService;
import tech.xs.auth.service.AuthPathRoleService;
import tech.xs.auth.service.AuthPathService;
import tech.xs.auth.service.AuthPathWhiteService;
import tech.xs.common.auth.domain.enums.AuthPathWhiteTypeEnum;
import tech.xs.common.framework.domain.config.FrameworkConfig;
import tech.xs.common.framework.domain.config.FrameworkServiceConfig;
import tech.xs.common.framework.domain.constant.Symbol;
import tech.xs.common.framework.domain.constant.mq.QueueNames;
import tech.xs.common.framework.domain.exception.BaseException;
import tech.xs.common.framework.domain.enums.DataSourceTypeEnum;
import tech.xs.common.domain.enums.YesNoEnum;
import tech.xs.common.framework.init.auth.domain.bo.sync.SyncAuthApiBo;
import tech.xs.common.framework.init.auth.domain.bo.sync.SyncAuthApiItemBo;
import tech.xs.common.framework.mq.MqMessageListener;
import tech.xs.common.framework.util.BaseEntityUtil;
import tech.xs.common.util.SyncUtil;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class AuthApiConsumer extends MqMessageListener<SyncAuthApiBo> {

    private static final String LOCK_AUTH_AUTH_PATH_KEY = "auth:lock:sync:auth:path";

    @Resource
    private RedissonClient redissonClient;
    @Resource
    private AuthPathWhiteService authPathWhiteService;
    @Resource
    private AuthPathPermissionService authPathPermissionService;
    @Resource
    private AuthPathRoleService authPathRoleService;
    @Resource
    private AuthPathService authPathService;
    @Resource
    private FrameworkConfig frameworkConfig;

    @Override
    public String getQueueName() {
        return QueueNames.AUTH_AUTH_PATH;
    }

    public void onMessage(SyncAuthApiBo syncApiBo) throws Exception {
        String content = JSON.toJSONString(syncApiBo);
        log.info("[MQ消费者][授权路径同步]内容:{}", content);
        List<SyncAuthApiItemBo> authPathList = syncApiBo.getApis();
        if (authPathList == null) {
            authPathList = new ArrayList<>();
        }
        String moduleName = syncApiBo.getModule();
        RLock lock = getSyncAuthPathWhiteLock(moduleName);
        try {
            setAuthApiItemPath(moduleName, authPathList);
            syncAuthPathWhite(moduleName, authPathList);
            syncAuthPathPermission(moduleName, authPathList);
            syncAuthPathRole(moduleName, authPathList);
            authPathService.reloadAllAuthPathCache();
        } catch (Exception e) {
            log.error("[MQ消费者][授权路径同步][moduleName:" + moduleName + "]异常 同步数据:" + content, e);
        } finally {
            lock.unlock();
        }
    }

    private void setAuthApiItemPath(String moduleName, List<SyncAuthApiItemBo> authPathList) {
        Collection<FrameworkServiceConfig> serviceList = frameworkConfig.getService().values();
        Map<String, FrameworkServiceConfig> modelMap = new HashMap<>();
        for (FrameworkServiceConfig serviceConfig : serviceList) {
            modelMap.put(serviceConfig.getName(), serviceConfig);
        }

        for (SyncAuthApiItemBo item : authPathList) {
            if (modelMap.containsKey(moduleName)) {
                FrameworkServiceConfig serviceConfig = modelMap.get(moduleName);
                if (StrUtil.isNotBlank(serviceConfig.getUrlPrefix())) {
                    item.setPath(serviceConfig.getUrlPrefix() + item.getPath());
                }
            } else {
                log.error("同步授权路径错误: 未找到模块前缀路径 {}", item);
            }
        }
    }

    private void syncAuthPathRole(String moduleName, List<SyncAuthApiItemBo> authPathList) {
        List<AuthPathRole> dbList = authPathRoleService.list(Wrappers.<AuthPathRole>lambdaQuery()
                .eq(AuthPathRole::getModule, moduleName)
                .eq(AuthPathRole::getSource, DataSourceTypeEnum.SystemGeneration)
        );
        List<AuthPathRole> addList = new ArrayList<>();
        List<AuthPathRole> updateList = new ArrayList<>();
        List<AuthPathRole> deleteList = new ArrayList<>();
        List<AuthPathRole> sourceList = new ArrayList<>();
        for (SyncAuthApiItemBo bo : authPathList) {
            List<String> roles = bo.getRoles();
            if (CollUtil.isEmpty(roles)) {
                continue;
            }
            for (String role : roles) {
                if (StrUtil.isBlank(role)) {
                    continue;
                }
                AuthPathRole authPathRole = new AuthPathRole();
                authPathRole.setPath(bo.getPath());
                authPathRole.setMethod(bo.getMethod());
                authPathRole.setModule(moduleName);
                authPathRole.setRoleCode(role);
                authPathRole.setStatus(YesNoEnum.YES);
                authPathRole.setSource(DataSourceTypeEnum.SystemGeneration);
                sourceList.add(authPathRole);
            }
        }

        SyncUtil.contrast(sourceList, dbList, addList, updateList, deleteList, new SyncUtil.Compare<>() {
            @Override
            public boolean compare(AuthPathRole o1, AuthPathRole o2) {
                return o1.getPath().equals(o2.getPath())
                        && o1.getMethod().equals(o2.getMethod())
                        && o1.getRoleCode().equals(o2.getRoleCode());
            }

            @Override
            public AuthPathRole sourceToTarget(AuthPathRole source, AuthPathRole target) {
                source.setId(target.getId());
                return source;
            }
        });

        if (!updateList.isEmpty()) {
            authPathRoleService.updateBatchById(updateList, 300);
        }
        if (!deleteList.isEmpty()) {
            authPathRoleService.removeBatchByIds(BaseEntityUtil.toIdList(deleteList), 300);
        }
        if (!addList.isEmpty()) {
            authPathRoleService.saveBatch(addList, 300);
        }
    }

    private void syncAuthPathPermission(String moduleName, List<SyncAuthApiItemBo> authPathList) {
        List<AuthPathPermission> dbList = authPathPermissionService.list(Wrappers.<AuthPathPermission>lambdaQuery()
                .eq(AuthPathPermission::getModule, moduleName)
                .eq(AuthPathPermission::getSource, DataSourceTypeEnum.SystemGeneration)
        );
        List<AuthPathPermission> addList = new ArrayList<>();
        List<AuthPathPermission> updateList = new ArrayList<>();
        List<AuthPathPermission> deleteList = new ArrayList<>();
        List<AuthPathPermission> sourceList = new ArrayList<>();
        for (SyncAuthApiItemBo bo : authPathList) {
            List<String> permissions = bo.getPermissions();
            if (CollUtil.isEmpty(permissions)) {
                continue;
            }
            for (String permission : permissions) {
                if (StrUtil.isBlank(permission)) {
                    continue;
                }
                AuthPathPermission authPathPermission = new AuthPathPermission();
                authPathPermission.setPath(bo.getPath());
                authPathPermission.setMethod(bo.getMethod());
                authPathPermission.setModule(moduleName);
                authPathPermission.setPermissionCode(permission);
                authPathPermission.setStatus(YesNoEnum.YES);
                authPathPermission.setSource(DataSourceTypeEnum.SystemGeneration);
                sourceList.add(authPathPermission);
            }
        }

        SyncUtil.contrast(sourceList, dbList, addList, updateList, deleteList, new SyncUtil.Compare<>() {
            @Override
            public boolean compare(AuthPathPermission o1, AuthPathPermission o2) {
                return o1.getPath().equals(o2.getPath())
                        && o1.getMethod().equals(o2.getMethod())
                        && o1.getPermissionCode().equals(o2.getPermissionCode());
            }

            @Override
            public AuthPathPermission sourceToTarget(AuthPathPermission source, AuthPathPermission target) {
                source.setId(target.getId());
                return source;
            }
        });

        if (!updateList.isEmpty()) {
            authPathPermissionService.updateBatchById(updateList, 300);
        }
        if (!deleteList.isEmpty()) {
            authPathPermissionService.removeBatchByIds(BaseEntityUtil.toIdList(deleteList), 300);
        }
        if (!addList.isEmpty()) {
            authPathPermissionService.saveBatch(addList, 300);
        }
    }

    /**
     * 同步授权白名单
     */
    private void syncAuthPathWhite(String moduleName, List<SyncAuthApiItemBo> authPathList) {
        List<AuthPathWhite> dbList = authPathWhiteService.list(Wrappers.<AuthPathWhite>lambdaQuery()
                .eq(AuthPathWhite::getModule, moduleName)
                .eq(AuthPathWhite::getSource, DataSourceTypeEnum.SystemGeneration)
        );
        List<AuthPathWhite> addList = new ArrayList<>();
        List<AuthPathWhite> updateList = new ArrayList<>();
        List<AuthPathWhite> deleteList = new ArrayList<>();
        List<AuthPathWhite> sourceList = new ArrayList<>();
        for (SyncAuthApiItemBo bo : authPathList) {
            if (AuthPathWhiteTypeEnum.EMPTY.equals(bo.getType())) {
                continue;
            }
            AuthPathWhite authPathWhite = new AuthPathWhite();
            authPathWhite.setPath(bo.getPath());
            authPathWhite.setMethod(bo.getMethod());
            authPathWhite.setType(bo.getType());
            authPathWhite.setModule(moduleName);
            authPathWhite.setStatus(YesNoEnum.YES);
            authPathWhite.setSource(DataSourceTypeEnum.SystemGeneration);
            sourceList.add(authPathWhite);
        }

        SyncUtil.contrast(sourceList, dbList, addList, updateList, deleteList, new SyncUtil.Compare<>() {
            @Override
            public boolean compare(AuthPathWhite o1, AuthPathWhite o2) {
                return o1.getPath().equals(o2.getPath())
                        && o1.getMethod().equals(o2.getMethod())
                        && o1.getType().equals(o2.getType());
            }

            @Override
            public AuthPathWhite sourceToTarget(AuthPathWhite source, AuthPathWhite target) {
                source.setId(target.getId());
                return source;
            }
        });

        if (!updateList.isEmpty()) {
            authPathWhiteService.updateBatchById(updateList, 300);
        }
        if (!deleteList.isEmpty()) {
            authPathWhiteService.removeBatchByIds(BaseEntityUtil.toIdList(deleteList), 300);
        }
        if (!addList.isEmpty()) {
            authPathWhiteService.saveBatch(addList, 300);
        }
    }

    public RLock getSyncAuthPathWhiteLock(String moduleName) throws InterruptedException {
        // 加锁,过期时间一分钟
        RLock lock = redissonClient.getLock(LOCK_AUTH_AUTH_PATH_KEY + Symbol.COLON + moduleName);
        if (lock.isLocked()) {
            if (!lock.tryLock(1, TimeUnit.MINUTES)) {
                throw new BaseException("获取授权路径白名单同步锁失败[moduleName:" + moduleName + "]");
            }
        }
        lock.lock(1, TimeUnit.MINUTES);
        return lock;
    }

}
